﻿using System;
using System.Diagnostics.Contracts;
using Microsoft.Practices.Unity;

namespace YasharEl.Infrastructure.ObjectFactory.Unity
{
    public class UnityDependencyContainer : BaseDependencyContainer
    {
        #region ReadOnly Fields

        protected readonly IUnityContainer unityContainer;

        #endregion

        #region Properties

        public IUnityContainer InnerContainer
        {
            get { return unityContainer; }
        }

        #endregion

        #region Constructors

        public UnityDependencyContainer(IUnityContainer unityContainer, string containerName = null)
        {
            Contract.Requires<ArgumentNullException>(unityContainer != null, "unityContainer could not be null.");
            this.unityContainer = unityContainer;
            if (string.IsNullOrWhiteSpace(containerName))
            {
                this.unityContainer.RegisterInstance<IDependencyContainer>(
                    this,
                    new ContainerControlledLifetimeManager());
                this.unityContainer.RegisterInstance<BaseDependencyContainer>(
                    this,
                    new ContainerControlledLifetimeManager());
            }
            else
            {
                this.unityContainer.RegisterInstance<IDependencyContainer>(
                    containerName,
                    this,
                    new ContainerControlledLifetimeManager());
                this.unityContainer.RegisterInstance<BaseDependencyContainer>(
                    containerName,
                    this,
                    new ContainerControlledLifetimeManager());
            }
            unityContainer.InitializeServiceLocator(containerName);
        }

        #endregion

        #region BaseDependencyContainer Abstract Overrides

        public override BaseDependencyContainer Parent
        {
            get
            {
                if (unityContainer.Parent == null)
                    return null;

                return unityContainer.Parent.Resolve<BaseDependencyContainer>();
            }
        }

        public override IDependencyContainer CreateChildContainer(string containerName = null)
        {
            IUnityContainer unityChildContainer = unityContainer.CreateChildContainer();
            UnityDependencyContainer unityDependencyContainer = new UnityDependencyContainer(
                unityChildContainer,
                containerName
            );
            unityDependencyContainer.InitializeContainer();

            if (string.IsNullOrEmpty(containerName))
                unityContainer.RegisterInstance<IUnityContainer>(unityChildContainer);
            else
                unityContainer.RegisterInstance<IUnityContainer>(containerName, unityChildContainer);

            return unityDependencyContainer;
        }

        public override IDependencyContainer FindChildContainer(string containerName)
        {
            ContainerRegistration containerRegistration = null;
            foreach (ContainerRegistration registration in unityContainer.Registrations)
            {
                if (registration.Name == containerName && registration.MappedToType == typeof(IUnityContainer))
                {
                    containerRegistration = registration;
                    break;
                }
            }

            if (containerRegistration == null)
                return null;

            IUnityContainer childUnityContainer = unityContainer.Resolve<IUnityContainer>(containerName);
            return childUnityContainer.Resolve<IDependencyContainer>(containerName);
        }

        public override IDependencyRegistrationTraverser CreateContainerTraverser()
        {
            return new UnityDependencyRegistrationTraverser(unityContainer);
        }

        protected override IDependencyResolver CreateDependencyResolver()
        {
            Contract.Requires<InvalidOperationException>(unityContainer != null, "unityContainer could not be null.");
            UnityDependencyResolver unityDependencyResolver = new UnityDependencyResolver(unityContainer);
            unityContainer.RegisterInstance<IDependencyResolver>(
                unityDependencyResolver,
                new ContainerControlledLifetimeManager());
            return unityDependencyResolver;
        }

        protected override IDependencyRegistrar CreateDependencyRegistrar()
        {
            Contract.Requires<InvalidOperationException>(unityContainer != null, "unityContainer could not be null.");
            UnityDependencyRegistrar unityDependencyRegistrar = new UnityDependencyRegistrar(unityContainer);
            unityContainer.RegisterInstance<IDependencyRegistrar>(
                unityDependencyRegistrar,
                new ContainerControlledLifetimeManager());
            return unityDependencyRegistrar;
        }

        #endregion
    }
}
